﻿#region

using System;
using System.Collections.Generic;
using System.Text;
using EsbMonitorMessages.ServiceDescription;
using MessageTypeDescriber.Utils;

#endregion

namespace MessageTypeDescriber.Xml
{
    [Serializable]
    public class TypeDescription : IEquatable<TypeDescription>
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public List<TypeDescription> GenericArguments { get; set; }

        protected TypeDescription()
        {
        }

        public TypeDescription(Type type)
        {
            Dictionary<Type, TypeDescription> mappedTypes = new Dictionary<Type, TypeDescription>();

            Init(type, mappedTypes);
        }

        private TypeDescription(Type type, IDictionary<Type, TypeDescription> mappedTypes)
        {
            Init(type, mappedTypes);
        }

        public string Signature
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(NameUtils.Short(Name));

                int count = GenericArguments.Count;
                for (int index = 0; index < count; index++)
                {
                    if (index == 0)
                    {
                        sb.Append("<");
                    }

                    sb.Append(GenericArguments[index].Signature);

                    if (index < count - 1)
                    {
                        sb.Append(", ");
                    }
                    else if (index == count - 1)
                    {
                        sb.Append(">");
                    }
                }

                return sb.ToString();
            }
        }

        private void Init(Type type, IDictionary<Type, TypeDescription> mappedTypes)
        {
            Name = type.FullName ?? type.Name;
            GenericArguments = new List<TypeDescription>();

            mappedTypes[type] = this;

            foreach (var genArg in type.GetGenericArguments())
            {
                GenericArguments.Add(mappedTypes.ContainsKey(genArg) ? mappedTypes[genArg] : new TypeDescription(genArg, mappedTypes));
            }

            Description[] description = (Description[])type.GetCustomAttributes(typeof(Description), false);

            if (description.Length > 0)
            {
                Description = description[0].Text;
            }
        }

        public bool Equals(TypeDescription other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return Equals(other.Name, Name) && Equals(other.GenericArguments, GenericArguments);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            if (obj.GetType() != typeof (TypeDescription))
            {
                return false;
            }
            return Equals((TypeDescription) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Name != null ? Name.GetHashCode() : 0)*397) ^ (GenericArguments != null ? GenericArguments.GetHashCode() : 0);
            }
        }

        public static bool operator ==(TypeDescription left, TypeDescription right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(TypeDescription left, TypeDescription right)
        {
            return !Equals(left, right);
        }
    }
}